import { useState, useEffect, lazy, useRef } from 'react'
/* -------------------------------------------------------- antd -- */
import { Upload } from 'antd'
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons'
/* -------------------------------------------------------- Utils -- */
import Utils from './utils'
/* -------------------------------------------------------- Declare -- */
const { $fn, $config, $http } = window
/* -------------------------------------------------------- Async Component -- */
const Button = lazy(()=>import('@antd/button'))
const getOld = (old, value) => {
	if(!$fn.hasArray(old)){
		return null
	}
	let x = $fn.deepCopy(old)
	x.forEach((v,i) => {
		value.forEach( k => {
			if( k.includes(v) || v.includes(k) ){
				x[i] = null
			}
		})
	})
	return x.filter(v => v )
}
/* -------------------------------------------------------- 回到顶部 -- */
const Index = ({ 
	className,						/** @param {String}				# 盒子 className */
	name, 							/** @param {String}				# name */
	label,							/** @param {String}				# 标签名 */
	// children,						/** @param {Component}			# slot */
	fileName,						/** @param {Component}			# 上传文件名 */
	//
	value,							/** @param {Number}				# 值 */
	disabled,						/** @param {Boolean}			# 禁用 */
	loading,						/** @param {Boolean}			# 加载 */
	mongo, 							/** @param {Boolean}			# 是否 mongodb 存储 */
	// 获取值
	onChange,						/** @param {Event}				# 有 Form 时值改变时执行 */
	onChanged,						/** @param {Event}				# 值改变时执行 */
	//
	// out,							/** @param {Number}				# 在外部设置 value 值 */
	fileType='img',					/** @param {String}				# 上传文件类型 */
	max=$config.fileMaxSize,		/** @param {String}				# 最大文件大小 M */
	maxCount = 10,					/** @param {String}				# 上传文件数量限制*/
	mul,							/** @param {Boolean}			# 是否多选 */
	showList=true,					/** @param {Boolean}			# 是否显示进度条 */
	directory,						/** @param {Boolean}			# 是否上传文件夹 */
	//
	api='upload',					/** @param {String}				# 请求接口 api */
	apis='uploads',					/** @param {String}				# 请求接口 api */
	param, 							/** @param {Object}				# 上传参数 */
	btnText, 						/** @param {String}				# 按钮名称 */
	size, 							/** @param {String}				# 按钮尺寸 */
	listType='picture-card', 		/** @param {String}				# 模式 */
	del=1, 							/** @param {Boolean}			# 是否删除修改文件 */
	// 自定义扩展
	antd = {},						/** @param {Object}				# antd */
	btn = {},
}, ref)=> {// eslint-disable-line
	const [ fileList, setFileList ] = useState([])
	const [ load, setLoad ] = useState( loading )
	const [ img, setImg ] = useState( )
	const onceRef = useRef(0)
	const valueRef = useRef(value)
	const isImg = listType === 'picture-card'
	if( !mul ) maxCount = 1
	if( directory) mul = true
	if(mongo){
		api = 'mg_upload'
		apis = 'mg_uploads'
	}
	useEffect(()=> {
		if(onceRef.current === 0){
			valueRef.current = value
			if(isImg){
				if( mongo){ // eslint-disable-line
					if( $fn.isString(value) || $fn.hasArray(value)){
						$http.pull(null, mul ? 'mg_upload_looks' : 'mg_upload_look', { param: { url:value } }).then(data=>{
							if(showList){
								setFileList( Utils.getFileList(data) )
							}else{
								setImg(data)
							}
						})
					}
				}else{
					if(showList){
						setFileList( Utils.getFileList(value) )
					}else{
						setImg( $fn.url($fn.hasArray( value ) ? value[0] : value) )
					}
				}
			}else{
				if( showList ){
					setFileList( Utils.getFileList(value) )
				}
			}
		}
	}, [ value ]) // eslint-disable-line
	const onUpload = mul ? null : ({ file, fileList }) => {
		let { status, response } = file
		if( status === 'uploading' ){		// 正在上传
			setFileList(fileList)
			setLoad(true)
		}else if( status === 'done' ){		// 上传完成
			let { data, msg, code } = response ?? {}
			// 上传成功
			if( code === 200 ){
				onceRef.current = 1
				$fn.msg.success('上传成功')
				let value = null
				if(fileType === 'img'){
					let img = null
					if( $fn.isString(data) ){
						value = img = data
					}else if( $fn.isObject(data) ){
						value = img = data.url
						if(mongo){
							value = data.id
							img = data.url
						}
					}
					if(showList) setFileList( Utils.getFileList( data, file ) )
					else setImg( $fn.url(img) )
				}else{
					setFileList( Utils.getFileList( data, file) )
					if( $fn.isString(data) ){
						value = data
					}else if( $fn.isObject(data) ){
						value = data.url
						if(mongo){
							value = data.id
						}
					}
				}
				
				const result = name ? { model: {[name]: value} } : null
				onChanged?.({ value, label, name, ...result  })
				onChange?.( del ? { [name]:value, [name + '_old']: valueRef.current } : value )
			}else if(code === $config.noLoginCode || code === $config.loginExpireCode){
				$fn.msg.error( msg )
				$fn.localRemove('user')
				$fn.recordSkip()
				//
				window?.$modalRef?.()?.close?.()
				window?.$formModalRef?.()?.close?.()
				window?.$tableModalRef?.()?.close?.()
				window?.$imageModalRef?.()?.close?.()
				window?.modal?.destroy?.()
				setTimeout(() => $fn.go('/login'), 200 )
			}else{
				$fn.msg.error( msg ?? '文件上传失败')
				setFileList( v => v.filter(v => v.url) )
			}
			setLoad(false)
		}else if( status === 'error' ){		// 上传出错
			$fn.msg.error(`${file.name} 文件上传失败!`)
			setFileList( v => v.filter(v => v.url) )
			setLoad(false)
		}	
	}
	const valid = file => {
		const delay = 5
		const type = Utils.getType(file.type)
		
		if($fn.hasArray(fileType)){
			let isOk = []
			let msg = ''
			fileType.forEach((v,i)=>{
				msg += (i === fileType.length-1) ? type[v].msg : type[v].msg + '、'
				if(type[v].bool){
					isOk.push(0)
				}
			})
			if (isOk.length === 0) {
				$fn.msg.warning(`仅允许上传${msg}!`, delay)
			}
			return isOk.length !== 0
		}else{
			if(['xls', 'video', 'img', 'video', 'pdf', 'doc'].includes(fileType)){
				const isOk = type[fileType].bool
				if (!isOk) {
					$fn.msg.warning(`仅允许上传 ${type[fileType].msg}!`, delay)
				}
				const isLt2M = file.size / 1024 / 1024 < max
				if (!isLt2M) {
					$fn.msg.warning(`${type[fileType].msg}必须小于 ${max}M!`, delay)
				}
				return isOk && isLt2M
			}else if( fileType === 'any'){
				return true
			}else{
				$fn.msg.warning(`未允许的上传文件类型`, delay)
				return false
			}
		}
	}
	// 上传之前判断
	const filesRef = useRef()
	const onBeforeUpload = (file, files) => {
		if( mul ){
			if( filesRef.current ){
				return false
			}
			filesRef.current = files
			let error = []
			files.forEach( v =>{
				let m = valid( v )
				if( !m ) error.push(m)
			})
			if(error.length === 0) {
				return true
			}else{
				filesRef.current = null
				return false
			}
		}else{
			return valid( file )
		}
	}
	// 自定义多图上传
	const customRequest = mul ? () => {
		if(filesRef.current){
			onceRef.current = 1
			$http.upload(null, apis, {
				param:filesRef.current, 
				loading:false,
				onLoading: d => setLoad(d)
			}).then( data => {
				setFileList( d => {
					data = Utils.getFileList( data )
					data = [ ...data, ...d ].map( v => ({ ...v, uid: v.uid || v.url }))		// 重新定义 uid
																// 将有 url 的数据过滤出来
					let value = []
					
					for(let i in data){
						let v = data[i]
						if(v.url){
							value.push( mongo ? v.uid : v.url )
						}
					}
					
					const result = name ? { model: {[name]: value} } : null
					onChanged?.({ value, label, name, ...result  })
					// 解决在呈现不同的组时无法更新组的问题
					setTimeout(()=>{
						let old = getOld(valueRef.current, value)
						onChange?.( del ? { [name]:value, [name + '_old']: old } : value )
					})		
					return data
				})
			} )
			filesRef.current = null
		}
	} : null
	
	const PlusComponent = () => load ? <LoadingOutlined className='f30' /> : <PlusOutlined className='f30'/>
	
	// useImperativeHandle( ref, () => ({
		
	// }))
	if( !fileName ){
		fileName = mul ? 'files[]' : 'file'
	}
	antd = {
		className,
		fileList,
		name: fileName,
		action: $config.api() + api,
		listType,
		multiple: mul,
		data: param,
		disabled: disabled || load,
		headers: {
			Authorization: $fn.getToken(),
		},
		beforeUpload: onBeforeUpload,
		showUploadList: showList,
		maxCount,
		directory,
		onChange:onUpload,
		customRequest,
		onPreview: ({ url, uid }) => {
			if( mongo ){
				$http.pull(null,'mg_upload_look', { param: { url: uid }, loading:1 }).then(data=>{
					if( Utils.isImage(uid) ){
						window.$imageModalRef().open({ url: data?.url })
					}else{
						window.open( data?.url )
					}
				})
			}else{
				if( Utils.isImage(url) ){
					window.$imageModalRef().open({ url })
				}else{
					window.open( url )
				}
			}
		},
		onRemove: file => {
			let value
			onceRef.current = 1
			setFileList( v => {
				const index = v.indexOf(file);
				const list = v.slice();
				list.splice(index, 1)
				value = list.map(v => mongo ? v.uid : v.url)
				
				if( !antd.multiple ){ value = null }
				
				const result = name ? { model: {[name]: value} } : null
				
				onChanged?.({ value, label, name, ...result  })
				
				let old = mul ? getOld(valueRef.current, value) : valueRef.current
				
				onChange?.( del ? { [name]:value, [name + '_old']: old } : value )
				
				return list
			})
		},
		...antd
	}
	return (
		<Upload {...antd}>
			{
				isImg ? (
					antd.multiple || showList ? <PlusComponent/> : (
						<>
							{img ? <img src={img} alt='上传图片' style={{ maxWidth: '100%',display:'inline-block' }} /> : <PlusComponent/>}
							{ (load && img) && <div className='abs_lt i10 fxmc' style={{width:104,height:104,background:'rgba(0,0,0,.2)'}}><LoadingOutlined style={{color:$config.mainColor, fontSize:30}} /></div> }
						</>
					)
				) : (
					<Button stop={false} loading={load} disabled={disabled} mode='upload' size={size} {...btn}>
						<span>{btnText ? btnText??'上传文件' : '上传' + label}</span>
					</Button>
				)
			}
		</Upload>
	)
}

export default Index